/*******************************************************\
|* Author: Trevor Sundberg
\*******************************************************/

// Includes
#include "SkugoTypeBinding.hpp"

namespace Skugo
{
  // Get the initializer list
  TypeBinding::InitList& TypeBinding::GetInitializers()
  {
    // Create a static list and return that
    static InitList initializers;
    return initializers;
  }

  // Add to the initializers (return an int so we can use it globally)
  int TypeBinding::AddInitializer(InitTypeFn fn)
  {
    // Add the function
    GetInitializers().push_back(fn);

    // Return a dummy int
    return 0;
  }

  // Initialize all queued types
  void TypeBinding::InitializeAllTypes()
  {
    // Get the intiailizers list
    InitList& initializers = GetInitializers();

    // Go through the entire list and invoke all initializers
    for (sizeType i = 0; i < initializers.size(); ++i)
    {
      // Invoke the initializer (nothing else to do but that!)
      initializers[i]();
    }

    // Clear the initializers
    initializers.clear();
  }

  // Check if a type is derived from a given base
  bool TypeBinding::IsDerivedFrom(const TypeInfo* type, const TypeInfo* base)
  {
    // Loop until the type chain becomes empty
    while (type != NULL)
    {
      // If the base type hash is the same as the current type hash...
      if (base == type)
        return true;

      // Iterate to the next parent
      type = type->Parent;
    }

    // Otherwise, the given type does not inherit from base
    return false;
  }

  // An efficient hash algorithm for c-strings strings
  int64 TypeBinding::HashString(const char* string, int64 length)
  {
    // Start the hash out at the length of the string
    int64 hash = length;
  
    // Compute a step so that way if the string is long, we don't iterate over single every character
    int64 step = (length >> 5) + 1;
  
    // Walk backwards through the string
    for (int64 i = length; i >= step; i -= step)
    {
      // Get the current character
      byte current = string[sizeType(i - 1)];

      // Randomize the bits of the hash, while using the string data
      hash = hash ^ ((hash << 5) + (hash >> 2) + current);
    }
  
    // Return the computed hash
    return hash;
  }

  // Create the type (the parent can be NULL if the type is a base)
  TypeInfo TypeBinding::MakeType
  (
    StringParam     name,
    sizeType        size,
    const TypeInfo* parent,
    SetupTypeFn     setup
  )
  {
    // Create the type info with the provided information
    TypeInfo newType;
    newType.Name     = name;
    newType.Parent   = parent;
    newType.Size     = size;
    newType.TypeGuid = TypeBinding::HashString(name.c_str(), name.size());

    // Invoke the setup function (where the user binds everything)
    setup(newType);

    // Return the new type
    return newType;
  }

  // Definition of all the primitive types
  SkugoDefinePrimitiveType(char);
  SkugoDefinePrimitiveType(short);
  SkugoDefinePrimitiveType(int);
  SkugoDefinePrimitiveType(long);
  SkugoDefinePrimitiveType(int64);
  SkugoDefinePrimitiveType(uchar);
  SkugoDefinePrimitiveType(ushort);
  SkugoDefinePrimitiveType(uint);
  SkugoDefinePrimitiveType(ulong);
  SkugoDefinePrimitiveType(uint64);
  SkugoDefinePrimitiveType(float);
  SkugoDefinePrimitiveType(double);
  SkugoDefinePrimitiveType(bool);
}
